home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Main.bin / ObjectOutputStream.java < prev    next >
Text File  |  1998-09-22  |  45KB  |  1,343 lines

  1. /*
  2.  * @(#)ObjectOutputStream.java    1.36 98/07/01
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  * 
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.io;
  16.  
  17. import java.util.Stack;
  18.  
  19. import sun.io.ObjectOutputStreamDelegate; // RMI over IIOP hook.
  20.  
  21. /**
  22.  * An ObjectOutputStream writes primitive data types and graphs of
  23.  * Java objects to an OutputStream.  The objects can be read
  24.  * (reconstituted) using an ObjectInputStream.
  25.  * Persistent storage of objects can be accomplished by using a file for
  26.  * the stream.
  27.  * If the stream is a network socket stream, the objects can be reconsituted
  28.  * on another host or in another process. <p>
  29.  *
  30.  * Only objects that support the java.io.Serializable interface can be
  31.  * written to streams.
  32.  *
  33.  * The class of each serializable object is encoded including the class
  34.  * name and signature of the class, the values of the
  35.  * object's fields and arrays, and the closure of any other objects
  36.  * referenced from the initial objects. <p>
  37.  * 
  38.  * The method <STRONG>writeObject</STRONG> is used to write an object
  39.  * to the stream.  Any object, including Strings and arrays, is
  40.  * written with writeObject. Multiple objects or primitives can be
  41.  * written to the stream.  The objects must be read back from the
  42.  * corresponding ObjectInputstream with the same types and in the same
  43.  * order as they were written.<p>
  44.  *
  45.  * Primitive data types can also be written to the stream using the
  46.  * appropriate methods from DataOutput. Strings can also be written
  47.  * using the writeUTF method.<p>
  48.  *
  49.  * The default serialization mechanism for an object writes the class
  50.  * of the object, the class signature, and the values of all
  51.  * non-transient and non-static fields.  References to other objects
  52.  * (except in transient or static fields) cause those objects to be
  53.  * written also. Multiple references to a single object are encoded
  54.  * using a reference sharing mechanism so that graphs of objects can
  55.  * be restored to the same shape as when the original was written. <p>
  56.  *
  57.  * For example to write an object that can be read by the example in ObjectInputStream: <br>
  58.  * <PRE>
  59.  *    FileOutputStream ostream = new FileOutputStream("t.tmp");
  60.  *    ObjectOutputStream p = new ObjectOutputStream(ostream);
  61.  *
  62.  *    p.writeInt(12345);
  63.  *    p.writeObject("Today");
  64.  *    p.writeObject(new Date());
  65.  *
  66.  *    p.flush();
  67.  *    ostream.close();
  68.  *
  69.  * </PRE>
  70.  *
  71.  * Classes that require special handling during the serialization and deserialization
  72.  * process must implement special methods with these exact signatures: <p>
  73.  *
  74.  * <PRE>
  75.  * private void readObject(java.io.ObjectInputStream stream)
  76.  *     throws IOException, ClassNotFoundException; 
  77.  * private void writeObject(java.io.ObjectOutputStream stream)
  78.  *     throws IOException
  79.  * </PRE><p>
  80.  * The writeObject method is responsible for writing the state of
  81.  * the object for its particular class so that the corresponding
  82.  * readObject method can restore it.
  83.  * The method does not need to concern itself with the
  84.  * state belonging to the object's superclasses or subclasses.
  85.  * State is saved by writing the individual fields to the ObjectOutputStream
  86.  * using the writeObject method or by using the methods for
  87.  * primitive data types supported by DataOutput. <p>
  88.  *
  89.  * Serialization does not write out the fields of any object that does
  90.  * not implement the java.io.Serializable interface.  Subclasses of
  91.  * Objects that are not serializable can be serializable. In this case
  92.  * the non-serializable class must have a no-arg constructor to allow
  93.  * its fields to be initialized.  In this case it is the
  94.  * responsibility of the subclass to save and restore the state of the
  95.  * non-serializable class. It is frequently the case that the fields
  96.  * of that class are accessible (public, package, or protected) or
  97.  * that there are get and set methods that can be used to restore the
  98.  * state. <p>
  99.  *
  100.  * Serialization of an object can be prevented by implementing writeObject
  101.  * and readObject methods that throw the NotSerializableException.
  102.  * The exception will be caught by the ObjectOutputStream and abort the
  103.  * serialization process.
  104.  *
  105.  * Implementing the Externalizable interface allows the object to
  106.  * assume complete control over the contents and format of the object's
  107.  * serialized form.  The methods of the Externalizable interface,
  108.  * writeExternal and readExternal, are called to save and restore the
  109.  * objects state.  When implemented by a class they can write and read
  110.  * their own state using all of the methods of ObjectOutput and
  111.  * ObjectInput.  It is the responsibility of the objects to handle any
  112.  * versioning that occurs.
  113.  *
  114.  * @author    Roger Riggs
  115.  * @version     1.36, 07/01/98
  116.  * @see java.io.DataOutput
  117.  * @see java.io.ObjectInputStream
  118.  * @see java.io.Serializable
  119.  * @see java.io.Externalizable
  120.  * @since       JDK1.1
  121.  */
  122. public class ObjectOutputStream
  123.     extends OutputStream
  124.     implements ObjectOutput, ObjectStreamConstants
  125.             
  126.     /** 
  127.      * Creates an ObjectOutputStream that writes to the specified OutputStream.
  128.      * The stream header is written to the stream. The caller may want to call
  129.      * flush immediately so that the corresponding ObjectInputStream can read
  130.      * the header immediately.
  131.      * @exception IOException Any exception thrown by the underlying OutputStream.
  132.      * @since     JDK1.1
  133.      */
  134.     public ObjectOutputStream(OutputStream out) throws IOException {
  135.  
  136.         /*
  137.          * RMI over IIOP hook. Check if we are a trusted subclass
  138.          * that has implemented the "sun.io.ObjectOutputStream"
  139.          * interface. If so, set our private flag that will be
  140.          * checked in "writeObject", "defaultWriteObject" and
  141.          * "enableReplaceObject". Note that we don't initialize
  142.          * private instance variables in this case as an optimization
  143.          * (subclasses using the hook should have no need for them).
  144.          */
  145.         
  146.         if (this instanceof sun.io.ObjectOutputStreamDelegate && this.getClass().getClassLoader() == null) {
  147.             isTrustedSubclass = true;
  148.             return;
  149.         }
  150.         
  151.     this.out = out;
  152.     dos = new DataOutputStream(this);
  153.     buf = new byte[1024];    // allocate buffer
  154.     writeStreamHeader();
  155.     resetStream();
  156.     }
  157.  
  158.     /**
  159.      * Write the specified object to the ObjectOutputStream.
  160.      * The class of the object, the signature of the class, and the values
  161.      * of the non-transient and non-static fields of the class and all
  162.      * of its supertypes are written.  Default serialization for a class can be
  163.      * overridden using the writeObject and the readObject methods. 
  164.      * Objects referenced by this object are written transitively so
  165.      * that a complete equivalent graph of objects can be
  166.      * reconstructed by an ObjectInputStream.  <p>
  167.      *
  168.      * Exceptions are thrown for
  169.      * problems with the OutputStream and for classes that should not be
  170.      * serialized.  All exceptions are fatal to the OutputStream, which
  171.      * is left in an indeterminate state, and it is up to the caller
  172.      * to ignore or recover the stream state.
  173.      * @exception InvalidClassException Something is wrong with a class used by
  174.      *       serialization.
  175.      * @exception NotSerializableException Some object to be serialized does not
  176.      *      implement the java.io.Serializable interface.
  177.      * @exception IOException Any exception thrown by the underlying OutputStream.
  178.      * @since     JDK1.1
  179.      */
  180.     public final void writeObject(Object obj)
  181.     throws IOException
  182.     {
  183.      
  184.     /*
  185.      * RMI over IIOP hook. Invoke delegate method if indicated.
  186.      */
  187.     if (isTrustedSubclass) {
  188.         ((ObjectOutputStreamDelegate) this).writeObjectDelegate(obj);
  189.         return;
  190.     }
  191.  
  192.     Object prevObject = currentObject;
  193.     ObjectStreamClass prevClassDesc = currentClassDesc;
  194.     boolean oldBlockDataMode = setBlockData(false);
  195.     recursionDepth++;
  196.  
  197.     try {
  198.         if (serializeNullAndRepeat(obj))
  199.         return;
  200.  
  201.         if (checkSpecialClasses(obj))
  202.         return;
  203.  
  204.  
  205.         /* If the replacment is enabled, give subclasses one chance
  206.          * to substitute a new object. If one is substituted,
  207.          * recheck for null, repeated refs, and special cased classes
  208.          */
  209.         if (enableReplace) {
  210.         Object altobj = replaceObject(obj);
  211.         if (obj != altobj) {
  212.  
  213.             if (altobj != null && !(altobj instanceof Serializable)) {
  214.             String clname = altobj.getClass().getName();
  215.             throw new NotSerializableException(clname);
  216.             }
  217.             
  218.             // If the alternate object is already
  219.             // serialized just remember the replacement
  220.             if (serializeNullAndRepeat(altobj)) {
  221.             addReplacement(obj, altobj);
  222.             return;
  223.             }
  224.  
  225.             /* Add this to the set of replaced objects.
  226.              * This must be done before the object is
  227.              * serialized so that if the object indirectly
  228.              * refers to the original it will be redirected to
  229.              * the replacement.
  230.              *
  231.              * NB: checkSpecialClasses should only call
  232.              * serializeNullandRepeat for objects that will not
  233.              * recurse.
  234.              */
  235.             addReplacement(obj, altobj);
  236.  
  237.             if (checkSpecialClasses(altobj))
  238.             return;
  239.  
  240.             obj = altobj;
  241.         }
  242.         }
  243.         if (checkSubstitutableSpecialClasses(obj))
  244.         return;
  245.         else {
  246.         /* Write out the object as itself */
  247.         outputObject(obj);
  248.         }
  249.     } catch (ObjectStreamException ee) {
  250.         if (abortIOException == null) {
  251.         try {
  252.             /* Prepare to write the exception to the stream.
  253.              * End blockdatamode in case it's set
  254.              * Write the exception code
  255.              * reset the stream to forget all previous objects
  256.              * write the exception that occurred
  257.              * reset the stream again so subsequent objects won't map to
  258.              * the exception or its args.
  259.              * Continue below to rethrow the exception.
  260.              */
  261.             setBlockData(false);
  262.  
  263.             writeCode(TC_EXCEPTION);
  264.             resetStream();
  265.             this.writeObject(ee);
  266.             resetStream();
  267.  
  268.             // Set the pending exception to be rethrown.
  269.             abortIOException = ee;
  270.         } catch (IOException fatal) {
  271.             /* An exception occurred while writing the original exception to
  272.              * the stream.  The original exception is not complete in
  273.              * the stream and recusion would be bad. Supercede the original
  274.              * Exception with a StreamCorruptedException using the message
  275.              * from this current exception.
  276.              */
  277.             abortIOException =
  278.             new StreamCorruptedException(fatal.getMessage());
  279.         }
  280.         }
  281.     } catch (IOException ee) {
  282.         // Don't supercede a pending exception, the original will be re-thrown.
  283.         if (abortIOException == null)
  284.         abortIOException = ee;
  285.         
  286.     } finally {
  287.         /* Restore state of previous call incase this is a nested call */
  288.         recursionDepth--;
  289.         currentObject = prevObject;
  290.         currentClassDesc = prevClassDesc;
  291.         setBlockData(oldBlockDataMode);
  292.     }
  293.     
  294.     /* If the recursion depth is 0, test for and clear the pending exception.
  295.      * If there is a pending exception throw it.
  296.      */
  297.     IOException pending = abortIOException;
  298.     if (recursionDepth == 0)
  299.         abortIOException = null;
  300.     if (pending != null) {
  301.         throw pending;
  302.     }
  303.     }
  304.     
  305.     /*
  306.      * Check for special cases of serializing objects.
  307.      */
  308.     private boolean checkSpecialClasses(Object obj) throws IOException {
  309.  
  310.     /*
  311.      * If this is a class, don't allow substitution
  312.      */
  313.     if (obj instanceof Class) {
  314.         outputClass((Class)obj);
  315.         return true;
  316.     }
  317.  
  318.     if (obj instanceof ObjectStreamClass) {
  319.         outputClassDescriptor((ObjectStreamClass)obj);
  320.         return true;
  321.     }
  322.  
  323.     return false;
  324.     }
  325.  
  326.     /*
  327.      * Check for special cases of substitutable serializing objects.
  328.      * These classes are replaceable.
  329.      */
  330.     private boolean checkSubstitutableSpecialClasses(Object obj)
  331.     throws IOException
  332.     {
  333.     if (obj instanceof String) {
  334.         outputString((String)obj);
  335.         return true;
  336.     }
  337.  
  338.     if (obj.getClass().isArray()) {
  339.         outputArray(obj);
  340.         return true;
  341.     }
  342.  
  343.     return false;
  344.     }
  345.  
  346.     /**
  347.      * Write the non-static and non-transient fields of the current class
  348.      * to this stream.  This may only be called from the writeObject method
  349.      * of the class being serialized. It will throw the NotActiveException
  350.      * if it is called otherwise.
  351.      * @since     JDK1.1
  352.      */
  353.     public final void defaultWriteObject() throws IOException {
  354.      
  355.     /*
  356.      * RMI over IIOP hook. Invoke delegate method if indicated.
  357.      */
  358.     if (isTrustedSubclass) {
  359.         ((ObjectOutputStreamDelegate) this).defaultWriteObjectDelegate();
  360.         return;
  361.     }
  362.          
  363.     if (currentObject == null || currentClassDesc == null)
  364.         throw new NotActiveException("defaultWriteObject");
  365.     
  366.     if (currentClassDesc.getFieldSequence() != null) {
  367.         boolean prevmode = setBlockData(false);
  368.         outputClassFields(currentObject, currentClassDesc.forClass(),
  369.                   currentClassDesc.getFieldSequence());
  370.         setBlockData(prevmode);
  371.     }
  372.     }
  373.     
  374.     /**
  375.      * Reset will disregard the state of any objects already written
  376.      * to the stream.  The state is reset to be the same as a new
  377.      * ObjectOutputStream.  The current point in the stream is marked
  378.      * as reset so the corresponding ObjectInputStream will be reset
  379.      * at the same point.  Objects previously written to the stream
  380.      * will not be refered to as already being in the stream.  They
  381.      * will be written to the stream again.
  382.      * @since     JDK1.1
  383.      */
  384.     public void reset() throws IOException {
  385.     if (currentObject != null || currentClassDesc != null)
  386.         throw new IOException("Illegal call to reset");
  387.     
  388.     /* Write a reset to the stream. */        
  389.     setBlockData(false);
  390.     writeCode(TC_RESET);
  391.     
  392.     resetStream();            // re-init the stream
  393.     abortIOException = null;
  394.     }
  395.  
  396.     /*
  397.      * Internal reset function to reinitialize the state of the stream.
  398.      * Reset state of things changed by using the stream.
  399.      */
  400.     private void resetStream() throws IOException {
  401.     if (wireHandle2Object == null) {
  402.         wireHandle2Object = new Object[100];
  403.         wireNextHandle = new int[100];
  404.         wireHash2Handle = new int[101];
  405.     } else {
  406.  
  407.         // Storage Optimization for frequent calls to reset method.
  408.         // Do not reallocate, only reinitialize.
  409.         for (int i = 0; i < nextWireOffset; i++) {
  410.         wireHandle2Object[i] = null;
  411.         wireNextHandle[i] = 0;
  412.         }
  413.     }
  414.       nextWireOffset = 0;
  415.       for (int i = 0; i < wireHash2Handle.length; i++) {
  416.           wireHash2Handle[i] = -1;
  417.       }
  418.     if (classDescStack == null)
  419.         classDescStack = new Stack();
  420.     else
  421.         classDescStack.setSize(0);
  422.     
  423.     for (int i = 0; i < nextReplaceOffset; i++)
  424.         replaceObjects[i] = null;
  425.     nextReplaceOffset = 0;
  426.     setBlockData(true);        /* Re-enable buffering */
  427.     }
  428.     
  429.     /**
  430.      * Subclasses may implement this method to allow class data to be stored
  431.      * in the stream. By default this method does nothing.
  432.      * The corresponding method in ObjectInputStream is resolveClass.
  433.      * This method is called exactly once for each unique class in the stream.
  434.      * The class name and signature will have already been written to the stream.
  435.      * This method may make free use of the ObjectOutputStream to save
  436.      * any representation of the class it deems suitable (for example,
  437.      * the bytes of the class file).  The resolveClass method in the corresponding
  438.      * subclass of ObjectInputStream must read and use any data or objects
  439.      * written by annotateClass. 
  440.      * annotateClass is called only for normal classes.  Arrays are not normal classes.
  441.      * @exception IOException Any exception thrown by the underlying OutputStream.
  442.      * @since     JDK1.1
  443.      */
  444.     protected void annotateClass(Class cl)
  445.     throws IOException
  446.     {
  447.     }
  448.  
  449.     /** This method will allow trusted subclasses of ObjectOutputStream
  450.      * to substitute one object for another during
  451.      * serialization. Replacing objects is disabled until
  452.      * enableReplaceObject is called. The enableReplaceObject method
  453.      * checks that the stream requesting to do replacment can be
  454.      * trusted. Every reference to serializable objects is passed to
  455.      * replaceObject.  To insure that the private state of objects is
  456.      * not unintentionally exposed only trusted streams may use
  457.      * replaceObject. <p>
  458.      *
  459.      * When a subclass is replacing objects it must insure that either
  460.      * a complementary substitution must be made during
  461.      * deserialization or that the substituted object is compatible
  462.      * with every field where the reference will be stored.  Objects
  463.      * whose type is not a subclass of the type of the field or array
  464.      * element abort the serialization by raising an exception and the
  465.      * object is not be stored. <p>
  466.      *
  467.      * This method is called only once when each object is first encountered.
  468.      * All subsequent references to the object will be redirected to the
  469.      * new object. This method should return the object to be substituted or
  470.      * the original object. <P>
  471.      *
  472.      * Null can be returned as the object to be substituted, but may
  473.      * cause NullReferenceException in classes that contain references
  474.      * to the original object since they may be expecting an object
  475.      * instead of null.<p>
  476.      *
  477.      * @exception IOException Any exception thrown by the underlying
  478.      * OutputStream.
  479.      * @since     JDK1.1
  480.      */
  481.     protected Object replaceObject(Object obj)
  482.     throws IOException
  483.     {
  484.     return obj;
  485.     }
  486.  
  487.     /**
  488.      * Enable the stream to do replacement of objects in the stream.
  489.      * If the stream is a trusted class it is allowed to enable replacement.
  490.      * Trusted classes are those classes with a classLoader equals null. <p>
  491.      * 
  492.      * When enabled the replaceObject method is called for every object
  493.      * being serialized.
  494.      * 
  495.      * @exception SecurityException The classloader of this stream object is non-null.
  496.      * @since     JDK1.1
  497.      */
  498.     protected final boolean enableReplaceObject(boolean enable)
  499.     throws SecurityException
  500.     {
  501.      
  502.     /*
  503.      * RMI over IIOP hook. Invoke delegate method if indicated.
  504.      */
  505.     if (isTrustedSubclass) {
  506.       return ((ObjectOutputStreamDelegate) this).enableReplaceObjectDelegate(enable);
  507.     }
  508.          
  509.     boolean previous = enableReplace;
  510.     if (enable) {
  511.         ClassLoader loader = this.getClass().getClassLoader();
  512.         if (loader == null) {
  513.         enableReplace = true;
  514.         return previous;
  515.         }
  516.         throw new SecurityException("Not trusted class");
  517.     } else {
  518.         enableReplace = false;
  519.     }
  520.     return previous;
  521.     }
  522.  
  523.     /**
  524.      * The writeStreamHeader method is provided so subclasses can
  525.      * append or prepend their own header to the stream.
  526.      * It writes the magic number and version to the stream.
  527.      * @since     JDK1.1
  528.      */
  529.     protected void writeStreamHeader() throws IOException {
  530.     writeShort(STREAM_MAGIC);
  531.     writeShort(STREAM_VERSION);
  532.     }
  533.  
  534.     /**
  535.      * Write a string to the stream.
  536.      * Note that since Strings are Objects, writeObject
  537.      * will behave identically.
  538.      */
  539.     private void outputString(String s) throws IOException {
  540.     /* Allocate a write handle but don't write it to the stream,
  541.      * Write out the code for a string,
  542.      * the read can regenerate the same sequence and it saves bytes.
  543.      */
  544.     assignWireOffset(s);
  545.     writeCode(TC_STRING);
  546.     writeUTF(s); 
  547.     }
  548.  
  549.  
  550.     /* Classes are special, they can not created during deserialization,
  551.      * but the appropriate class can be found. 
  552.      */
  553.     private void outputClass(Class aclass) throws IOException {
  554.  
  555.     writeCode(TC_CLASS);
  556.     /* Find the class descriptor and write it out */
  557.     ObjectStreamClass v = ObjectStreamClass.lookup(aclass);
  558.  
  559.     if (v == null)
  560.         throw new NotSerializableException(aclass.getName());
  561.  
  562.     outputClassDescriptor(v);
  563.  
  564.     assignWireOffset(aclass);
  565.     }
  566.   
  567.  
  568.     /* Write the class descriptor */
  569.     private void outputClassDescriptor(ObjectStreamClass classdesc) 
  570.     throws IOException
  571.     {
  572.     if (serializeNullAndRepeat(classdesc))
  573.         return;
  574.  
  575.     /* Write out the code for a class
  576.      * Write out the class name and its serialVersionUID
  577.      */
  578.     writeCode(TC_CLASSDESC);
  579.     String classname = classdesc.getName();
  580.  
  581.     writeUTF(classname);
  582.     writeLong(classdesc.getSerialVersionUID());
  583.  
  584.     /* This is done here to be symetric with the inputClass method
  585.      * Since the resolveClassName() method may use the stream.
  586.      * The assignments of wirehandles must be done in the same order
  587.      */
  588.     assignWireOffset(classdesc);
  589.  
  590.     /* Write the version description for this class */
  591.     classdesc.write(this);
  592.  
  593.     /* Give subclassers a chance to add the class implementation
  594.      * to the stream.  Set BlockData mode so any information they
  595.      * write can be skipped on reading.
  596.      */
  597.     boolean prevMode = setBlockData(true);
  598.     annotateClass(classdesc.forClass());
  599.     setBlockData(prevMode);
  600.     writeCode(TC_ENDBLOCKDATA);
  601.  
  602.     /*
  603.      * Write out the superclass descriptor of this descriptor
  604.      * only if it is for a java.io.Serializable class.
  605.      * else write null.
  606.      */
  607.     ObjectStreamClass superdesc = classdesc.getSuperclass();
  608.     outputClassDescriptor(superdesc);
  609.     }
  610.     
  611.     /**
  612.      * Write an array out. Note that since Arrays are Objects, writeObject(obj)
  613.      * will behave identically. <br><br>
  614.      * @param o can represent an array of any type/dimension.
  615.      */
  616.     private void outputArray(Object obj)
  617.     throws IOException
  618.     {
  619.     Class currclass = obj.getClass();
  620.  
  621.     ObjectStreamClass v = ObjectStreamClass.lookup(currclass);
  622.  
  623.     /* Write out the code for an array and the name of the class */
  624.     writeCode(TC_ARRAY);
  625.     outputClassDescriptor(v);
  626.  
  627.     /* Assign the wirehandle for this object and outputArrayValues
  628.      * writes the length and the array contents.
  629.      */
  630.     assignWireOffset(obj);
  631.  
  632.     int i, length;
  633.     Class type = currclass.getComponentType();
  634.  
  635.     if (type.isPrimitive()) {
  636.         /* Write arrays of primitive types using the DataOutput
  637.          * methods that convert each element into the output buffer.
  638.          * The data types are ordered by the frequency
  639.          * in which they are expected to occur.
  640.          */
  641.         if (type == Integer.TYPE) {
  642.         int[] array = (int[])obj;
  643.         length = array.length;
  644.         writeInt(length);
  645.         for (i = 0; i < length; i++) {
  646.             writeInt(array[i]);
  647.         }
  648.         } else if (type == Byte.TYPE) {
  649.         byte[] array = (byte[])obj;
  650.         length = array.length;
  651.         writeInt(length);
  652.         writeInternal(array, 0, length, true);
  653.         } else if (type == Long.TYPE) {
  654.         long[] array = (long[])obj;
  655.         length = array.length;
  656.         writeInt(length);
  657.         for (i = 0; i < length; i++) {
  658.             writeLong(array[i]);
  659.         }
  660.         } else if (type == Float.TYPE) {
  661.         float[] array = (float[])obj;
  662.         length = array.length;
  663.         writeInt(length);
  664.         for (i = 0; i < length; i++) {
  665.             writeFloat(array[i]);
  666.         }
  667.         } else if (type == Double.TYPE) {
  668.         double[] array = (double[])obj;
  669.         length = array.length;
  670.         writeInt(length);
  671.         for (i = 0; i < length; i++) {
  672.             writeDouble(array[i]);
  673.         }
  674.         } else if (type == Short.TYPE) {
  675.         short[] array = (short[])obj;
  676.         length = array.length;
  677.         writeInt(length);
  678.         for (i = 0; i < length; i++) {
  679.             writeShort(array[i]);
  680.         }
  681.         } else if (type == Character.TYPE) {
  682.         char[] array = (char[])obj;
  683.         length = array.length;
  684.         writeInt(length);
  685.         for (i = 0; i < length; i++) {
  686.             writeChar(array[i]);
  687.         }
  688.         } else if (type == Boolean.TYPE) {
  689.         boolean[] array = (boolean[])obj;
  690.         length = array.length;
  691.         writeInt(length);
  692.         for (i = 0; i < length; i++) {
  693.             writeBoolean(array[i]);
  694.         }
  695.         } else {
  696.         throw new InvalidClassException(currclass.getName());
  697.         }
  698.     } else {
  699.         Object[] array = (Object[])obj;
  700.         length = array.length;
  701.         writeInt(length);
  702.         for (i = 0; i < length; i++) {
  703.         writeObject(array[i]);
  704.         }
  705.     }
  706.     }
  707.  
  708.     /*
  709.      * Put the object into the stream The newObject code is written
  710.      * followed by the ObjectStreamClass for the object's class.  Each
  711.      * of the objects classes is written using the default
  712.      * serialization code and dispatching to Specials where
  713.      * appropriate.
  714.      */
  715.     private void outputObject(Object obj)
  716.     throws IOException
  717.     {
  718.     currentObject = obj;
  719.     Class currclass = obj.getClass();
  720.  
  721.     /* Get the Class descriptor for this class,
  722.      * Throw a NotSerializableException if there is none.
  723.      */
  724.     currentClassDesc = ObjectStreamClass.lookup(currclass);
  725.     if (currentClassDesc == null) {
  726.         throw new NotSerializableException(currclass.getName());
  727.     }
  728.  
  729.     /* Write the code to expect an instance and
  730.      * the class descriptor of the instance
  731.      */
  732.     writeCode(TC_OBJECT);
  733.     outputClassDescriptor(currentClassDesc);
  734.  
  735.     /* Assign the next wirehandle */
  736.     assignWireOffset(obj);
  737.  
  738.     /* If the object is externalizable,
  739.      * call writeExternal.
  740.      * else do Serializable processing.
  741.      */
  742.     if (currentClassDesc.isExternalizable()) {
  743.         Externalizable ext = (Externalizable)obj;
  744.         ext.writeExternal(this);
  745.     } else {
  746.  
  747.         /* The object's classes should be processed from supertype to subtype
  748.          * Push all the clases of the current object onto a stack.
  749.          * Remember the stack pointer where this set of classes is being pushed.
  750.          */
  751.         int stackMark = classDescStack.size();
  752.         try {
  753.         ObjectStreamClass next;
  754.         while ((next = currentClassDesc.getSuperclass()) != null) {
  755.             classDescStack.push(currentClassDesc);
  756.             currentClassDesc = next;
  757.         }
  758.  
  759.         /* 
  760.          * For currentClassDesc and all the pushed class descriptors
  761.          *    If the class is writing its own data
  762.          *          set blockData = true; call the class writeObject method
  763.          *    If not
  764.          *     invoke either the defaultWriteObject method.
  765.          */
  766.         do {
  767.             if (currentClassDesc.hasWriteObject()) {
  768.             setBlockData(true); /* Block any data the class writes */
  769.             invokeObjectWriter(obj, currentClassDesc.forClass());
  770.             setBlockData(false);
  771.             writeCode(TC_ENDBLOCKDATA);
  772.             } else {
  773.             defaultWriteObject();
  774.             }
  775.         } while (classDescStack.size() > stackMark &&
  776.              (currentClassDesc = (ObjectStreamClass)classDescStack.pop()) != null);
  777.         } finally {
  778.         classDescStack.setSize(stackMark);
  779.         }
  780.     }
  781.     }
  782.     
  783.     /* Serialize the reference if it is NULL or is for an object that
  784.      * was already replaced or already serialized.
  785.      * If the object was already replaced, look for the replacement
  786.      * object in the known objects and if found, write its handle
  787.      * Return True if the reference is either null or a repeat.
  788.      */
  789.     private boolean serializeNullAndRepeat(Object obj)
  790.     throws IOException
  791.     {
  792.         if (obj == null) {
  793.         writeCode(TC_NULL);
  794.         return true;
  795.     }
  796.     /* Look to see if this object has already been replaced.
  797.      * If so, proceed using the replacement object.
  798.      */
  799.     if (replaceObjects != null) {
  800.         for (int i = 0; i < nextReplaceOffset; i+= 2) {
  801.         if (replaceObjects[i] == obj) {
  802.             obj = replaceObjects[i+1];
  803.             break;
  804.         }
  805.         }
  806.     }
  807.  
  808.     int handle = findWireOffset(obj);
  809.     if (handle >= 0) {
  810.         /* Add a reference to the stream */
  811.         writeCode(TC_REFERENCE);
  812.         writeInt(handle + baseWireHandle);
  813.         return true;
  814.     }
  815.     return false;        // not serialized, its up to the caller
  816.     }
  817.  
  818.     /*
  819.      * Locate and return if found the handle for the specified object.
  820.      * -1 is returned if the object does not occur in the array of
  821.      * known objects. 
  822.      */
  823.     private int findWireOffset(Object obj) {
  824.     int hash = System.identityHashCode(obj);
  825.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  826.  
  827.     for (int handle = wireHash2Handle[index];
  828.          handle >= 0;
  829.          handle = wireNextHandle[handle]) {
  830.         
  831.         if (wireHandle2Object[handle] == obj)
  832.         return handle;
  833.     }
  834.     return -1;
  835.     }
  836.  
  837.     /* Allocate a handle for an object.
  838.      * The Vector is indexed by the wireHandleOffset
  839.      * and contains the object.
  840.      */
  841.     private void assignWireOffset(Object obj)
  842.     throws IOException
  843.     {
  844.     // Extend the array if there isn't room for this new element
  845.     if (nextWireOffset == wireHandle2Object.length) {
  846.         Object[] oldhandles = wireHandle2Object;
  847.         wireHandle2Object = new Object[nextWireOffset*2];
  848.         System.arraycopy(oldhandles, 0,
  849.                  wireHandle2Object, 0,
  850.                  nextWireOffset);
  851.         int[] oldnexthandles = wireNextHandle;
  852.         wireNextHandle = new int[nextWireOffset*2];
  853.         System.arraycopy(oldnexthandles, 0,
  854.                  wireNextHandle, 0,
  855.                  nextWireOffset);
  856.         // TBD: Rehash the hash array if necessary
  857.     }
  858.     wireHandle2Object[nextWireOffset] = obj;
  859.  
  860.     hashInsert(obj, nextWireOffset);
  861.  
  862.     nextWireOffset++;
  863.     return;
  864.     }
  865.  
  866.  
  867.     /*
  868.      * Insert the specified object into the hash array and link if
  869.      * necessary. Put the new object into the hash table and link the
  870.      * previous to it. Newer objects occur earlier in the list.
  871.      */
  872.     private void hashInsert(Object obj, int offset) {
  873.     int hash = System.identityHashCode(obj);
  874.     int index = (hash & 0x7FFFFFFF) % wireHash2Handle.length;
  875.     wireNextHandle[offset] = wireHash2Handle[index];
  876.     wireHash2Handle[index] = offset;
  877.     }
  878.  
  879.     /*
  880.      * Add a replacement object to the table.
  881.      * The even numbered indices are the original objects.
  882.      * The odd numbered indices are the replacement objects.
  883.      *
  884.      */
  885.     private void addReplacement(Object orig, Object replacement) {
  886.     // Extend the array if there isn't room for this new element
  887.  
  888.     if (replaceObjects == null) {
  889.         replaceObjects = new Object[10];
  890.     }
  891.     if (nextReplaceOffset == replaceObjects.length) {
  892.         Object[] oldhandles = replaceObjects;
  893.         replaceObjects = new Object[2+nextReplaceOffset*2];
  894.         System.arraycopy(oldhandles, 0,
  895.                  replaceObjects, 0,
  896.                  nextReplaceOffset);
  897.     }
  898.     replaceObjects[nextReplaceOffset++] = orig;
  899.     replaceObjects[nextReplaceOffset++] = replacement;
  900.     }
  901.  
  902.     /* Write out the code indicating the type what follows.
  903.      * See ObjectStreamConstants for definitions.
  904.      */
  905.     private void writeCode(int tag)
  906.     throws IOException
  907.     {
  908.     writeByte(tag);
  909.     }
  910.  
  911.     /*
  912.      * Implement the OutputStream methods.  The stream has
  913.      * two modes used internally to ObjectOutputStream.  When
  914.      * in BlockData mode, all writes are buffered and written
  915.      * to the underlying stream prefixed by a code and length.
  916.      * When not in BlockData mode (false), writes pass directly
  917.      * through to the underlying stream.
  918.      * The BlockData mode is used to encapsulate data written
  919.      * by class specific writeObject methods that is intended
  920.      * only to be read by corresponding readObject method of the 
  921.      * same class.  The blocking of data allows it to be skipped
  922.      * if necessary.
  923.      *
  924.      * The setBlockData method is used to switch buffering
  925.      * on and off.  When switching between on and off
  926.      * the buffer is drained.
  927.      *
  928.      * The actual buffering is very similar to that of
  929.      * BufferedOutputStream but BufferedOutputStream can
  930.      * write to the underlying stream without the headers.
  931.      */
  932.     private boolean blockDataMode;    /* true to buffer and block data */
  933.     private byte[] buf;        /* byte array of buffered data. */
  934.     private int count;        /* count of bytes in the buffer */
  935.     private OutputStream out;    /* Stream to write the data to */
  936.  
  937.     /**
  938.      * Writes a byte. This method will block until the byte is actually
  939.      * written.
  940.      * @param b    the byte
  941.      * @exception IOException If an I/O error has occurred.
  942.      * @since     JDK1.1
  943.      */
  944.     public void write(int data) throws IOException {
  945.     
  946.     if (count >= buf.length)
  947.         drain();        /* Drain, make room for more */
  948.     buf[count++] = (byte)data;
  949.     }
  950.  
  951.     /**
  952.      * Writes an array of bytes. This method will block until the bytes
  953.      * are actually written.
  954.      * @param b    the data to be written
  955.      * @exception IOException If an I/O error has occurred.
  956.      * @since     JDK1.1
  957.      */
  958.     public void write(byte b[]) throws IOException {
  959.     write(b, 0, b.length);
  960.     }
  961.  
  962.     /*
  963.      * Writes a sub array of bytes. 
  964.      * @param b    the data to be written
  965.      * @param off    the start offset in the data
  966.      * @param len    the number of bytes that are written
  967.      * @param copyOnWrite do not expose b to overrides of ObjectStream.write,
  968.      *                    copy the contents of b to a buffer before writing.
  969.      * @exception IOException If an I/O error has occurred.
  970.      * @since     JDK1.1
  971.      */
  972.     private void writeInternal(byte b[], int off, int len, 
  973.                    boolean copyOnWrite) throws IOException {
  974.     if (len < 0)
  975.         throw new IndexOutOfBoundsException();
  976.  
  977.     /*
  978.      * If array will fit in output buffer, copy it in there; otherwise,
  979.      * drain anything in the buffer and send it through to underlying
  980.      * output stream directly.
  981.      */
  982.     int avail = buf.length - count;
  983.     if (len <= avail) {
  984.         System.arraycopy(b, off, buf, count, len);
  985.         count += len;
  986.     } else if (! blockDataMode && copyOnWrite) {
  987.         bufferedWrite(b, off, len);    
  988.     } else {
  989.         drain();
  990.         if (blockDataMode) {
  991.         if (len <= 255) {
  992.             out.write(TC_BLOCKDATA);
  993.             out.write(len);
  994.         } else {
  995.             // use block data with int size if necessary
  996.             out.write(TC_BLOCKDATALONG);
  997.             // send 32 bit int directly to underlying stream
  998.             out.write((len >> 24) & 0xFF);
  999.             out.write((len >> 16) & 0xFF);
  1000.             out.write((len >>  8) & 0xFF);
  1001.             out.write(len & 0xFF);
  1002.         }
  1003.         }
  1004.         out.write(b, off, len);
  1005.     }
  1006.     }
  1007.  
  1008.  
  1009.     /**
  1010.      * Flushes the stream. This will write any buffered
  1011.      * output bytes and flush through to the underlying stream.
  1012.      * @exception IOException If an I/O error has occurred.
  1013.      * @since     JDK1.1
  1014.      */
  1015.     public void flush() throws IOException {
  1016.     drain();
  1017.     out.flush();
  1018.     }
  1019.  
  1020.     /**
  1021.      * Drain any buffered data in ObjectOutputStream.  Similar to flush
  1022.      * but does not propagate the flush to the underlaying stream.
  1023.      * @since     JDK1.1
  1024.      */
  1025.     protected void drain() throws IOException {
  1026.     /*
  1027.      * Drain the data buffer.
  1028.      * If the blocking mode is on, prepend the buffer
  1029.      * with the code for a blocked data and the length.
  1030.      * The code is TC_BLOCKDATA and the length is < 255 so it fits
  1031.      * in a byte. 
  1032.      */
  1033.     if (count == 0)
  1034.         return;
  1035.  
  1036.     /* If in blockdata mode, write the header with the count.
  1037.      * If the count is < 256, use the short header form.
  1038.      * othewise use the long form.
  1039.      */
  1040.     if (blockDataMode) {
  1041.         if (count <= 255) {
  1042.         out.write(TC_BLOCKDATA);
  1043.         out.write(count);
  1044.         } else {
  1045.         out.write(TC_BLOCKDATALONG);
  1046.         out.write((count >> 24) & 0xFF);
  1047.         out.write((count >> 16) & 0xFF);
  1048.         out.write((count >>  8) & 0xFF);
  1049.         out.write(count & 0xFF);
  1050.         }
  1051.     }
  1052.     out.write(buf, 0, count);
  1053.     count = 0;
  1054.     }
  1055.  
  1056.     /**
  1057.      * Closes the stream. This method must be called
  1058.      * to release any resources associated with the
  1059.      * stream.
  1060.      * @exception IOException If an I/O error has occurred.
  1061.      * @since     JDK1.1
  1062.      */
  1063.     public void close() throws IOException {
  1064.     flush();        /* Make sure we're not holding any data */
  1065.     out.close();
  1066.     }
  1067.  
  1068.     /*
  1069.      * Set the blockData mode,  if it turned from on to off
  1070.      * the buffer is drained.  The previous mode is returned.
  1071.      */
  1072.     private boolean setBlockData(boolean mode) throws IOException {
  1073.     if (blockDataMode == mode)
  1074.         return mode;
  1075.     drain();
  1076.     blockDataMode = mode;
  1077.     return !mode;        /* previous value was the opposite */
  1078.     }
  1079.     
  1080.     /* -------------------------------------------------------------- */
  1081.     /*
  1082.      * Provide the methods to implement DataOutput.
  1083.      * These are copied from DataOutputStream to avoid the overhead
  1084.      * of multiple method calls and to buffer the data directly.
  1085.      */
  1086.     private DataOutputStream dos;
  1087.  
  1088.     /**
  1089.      * Writes a boolean.
  1090.      * @param data the boolean to be written
  1091.      * @since     JDK1.1
  1092.      */
  1093.     public void writeBoolean(boolean data) throws IOException {
  1094.     if (count >= buf.length)
  1095.         drain();
  1096.     buf[count++] = (byte)(data ? 1 : 0);
  1097.  
  1098.     }
  1099.  
  1100.     /**
  1101.      * Writes a sub array of bytes. 
  1102.      * @param b    the data to be written
  1103.      * @param off    the start offset in the data
  1104.      * @param len    the number of bytes that are written
  1105.      * @exception IOException If an I/O error has occurred.
  1106.      * @since     JDK1.1
  1107.      */
  1108.     public void write(byte b[], int off, int len) throws IOException {
  1109.     writeInternal(b, off, len, false);
  1110.     }
  1111.  
  1112.     /* Use write buffering of byte[] b to prevent exposure of
  1113.      * 'b' reference to untrusted overrides of ObjectOutput.write(byte[]).
  1114.      * 
  1115.      * NOTE: Method is only intended for protecting serializable byte []
  1116.      *       fields written by default serialization. Thus, it can 
  1117.      *       never get called while in blockDataMode.
  1118.      */
  1119.     private void bufferedWrite(byte b[], int off, int len) throws IOException {
  1120.     int bufAvail = buf.length - count;
  1121.     int bytesToWrite = len;
  1122.     
  1123.     // Handle case where byte array is larger than available buffer.
  1124.     if (bytesToWrite > bufAvail) {
  1125.  
  1126.         // Logically: fill rest of 'buf' with 'b' and drain.
  1127.         System.arraycopy(b, off, buf, count, bufAvail);
  1128.         off += bufAvail;
  1129.         bytesToWrite -= bufAvail;
  1130.         out.write(buf, 0, buf.length);
  1131.         count = 0;
  1132.  
  1133.         // Write out buf.length chunks of byte array.
  1134.         while (bytesToWrite >= buf.length) {
  1135.         System.arraycopy(b, off, buf, 0, buf.length);
  1136.         out.write(buf, 0, buf.length);
  1137.         off += buf.length;
  1138.         bytesToWrite -= buf.length;
  1139.  
  1140.         // Optimization: do not modify or access "count" in this loop.
  1141.         }
  1142.     }
  1143.  
  1144.     // Put remainder of byte array b into buffer. 
  1145.     if (bytesToWrite != 0) {
  1146.         System.arraycopy(b, off, buf, count, bytesToWrite);
  1147.         count += bytesToWrite;
  1148.     }
  1149.     }
  1150.  
  1151.     /**
  1152.      * Writes an 8 bit byte.
  1153.      * @param data the byte value to be written
  1154.      * @since     JDK1.1
  1155.      */
  1156.     public void writeByte(int data) throws IOException  {
  1157.     if (count >= buf.length)
  1158.         drain();
  1159.     buf[count++] = (byte)data;
  1160.     }
  1161.  
  1162.     /**
  1163.      * Writes a 16 bit short.
  1164.      * @param data the short value to be written
  1165.      * @since     JDK1.1
  1166.      */
  1167.     public void writeShort(int data)  throws IOException {
  1168.     if (count + 2 > buf.length)
  1169.         drain();
  1170.     buf[count++] = (byte)((data >>>  8));
  1171.     buf[count++] = (byte)((data >>>  0));
  1172.     }
  1173.  
  1174.     /**
  1175.      * Writes a 16 bit char.
  1176.      * @param data the char value to be written
  1177.      * @since     JDK1.1
  1178.      */
  1179.     public void writeChar(int data)  throws IOException {
  1180.     if (count + 2 > buf.length)
  1181.         drain();
  1182.     buf[count++] = (byte)((data >>>  8));
  1183.     buf[count++] = (byte)((data >>>  0));
  1184.     }
  1185.  
  1186.     /**
  1187.      * Writes a 32 bit int.
  1188.      * @param data the integer value to be written
  1189.      * @since     JDK1.1
  1190.      */
  1191.     public void writeInt(int data)  throws IOException {
  1192.     if (count + 4 > buf.length)
  1193.         drain();
  1194.     buf[count++] = (byte)((data >>> 24));
  1195.     buf[count++] = (byte)((data >>> 16));
  1196.     buf[count++] = (byte)((data >>>  8));
  1197.     buf[count++] = (byte)((data >>>  0));
  1198.     }
  1199.  
  1200.     /**
  1201.      * Writes a 64 bit long.
  1202.      * @param data the long value to be written
  1203.      * @since     JDK1.1
  1204.      */
  1205.     public void writeLong(long data)  throws IOException {
  1206.     if (count + 8 > buf.length)
  1207.         drain();
  1208.     buf[count++] = (byte)((int)(data >>> 56));
  1209.     buf[count++] = (byte)((int)(data >>> 48));
  1210.     buf[count++] = (byte)((int)(data >>> 40));
  1211.     buf[count++] = (byte)((int)(data >>> 32));
  1212.     buf[count++] = (byte)((data >>> 24));
  1213.     buf[count++] = (byte)((data >>> 16));
  1214.     buf[count++] = (byte)((data >>>  8));
  1215.     buf[count++] = (byte)((data >>>  0));
  1216.     }
  1217.  
  1218.     /**
  1219.      * Writes a 32 bit float.
  1220.      * @param data the float value to be written
  1221.      * @since     JDK1.1
  1222.      */
  1223.     public void writeFloat(float data) throws IOException {
  1224.     int value = Float.floatToIntBits(data);
  1225.     if (count + 4 > buf.length)
  1226.         drain();
  1227.     buf[count++] = (byte)((value >>> 24));
  1228.     buf[count++] = (byte)((value >>> 16));
  1229.     buf[count++] = (byte)((value >>>  8));
  1230.     buf[count++] = (byte)((value >>>  0));
  1231.     }
  1232.  
  1233.     /**
  1234.      * Writes a 64 bit double.
  1235.      * @param data the double value to be written
  1236.      * @since     JDK1.1
  1237.      */
  1238.     public void writeDouble(double data) throws IOException {
  1239.     long value = Double.doubleToLongBits(data);
  1240.     if (count + 8 > buf.length)
  1241.         drain();
  1242.     buf[count++] = (byte)((int)(value >>> 56));
  1243.     buf[count++] = (byte)((int)(value >>> 48));
  1244.     buf[count++] = (byte)((int)(value >>> 40));
  1245.     buf[count++] = (byte)((int)(value >>> 32));
  1246.     buf[count++] = (byte)((value >>> 24));
  1247.     buf[count++] = (byte)((value >>> 16));
  1248.     buf[count++] = (byte)((value >>>  8));
  1249.     buf[count++] = (byte)((value >>>  0));
  1250.     }
  1251.  
  1252.     /**
  1253.      * Writes a String as a sequence of bytes.
  1254.      * @param s the String of bytes to be written
  1255.      * @since     JDK1.1
  1256.      */
  1257.     public void writeBytes(String data) throws IOException {
  1258.     dos.writeBytes(data);
  1259.     }
  1260.  
  1261.     /**
  1262.      * Writes a String as a sequence of chars.
  1263.      * @param s the String of chars to be written
  1264.      * @since     JDK1.1
  1265.      */
  1266.     public void writeChars(String data) throws IOException {
  1267.     dos.writeChars(data);
  1268.     }
  1269.  
  1270.     /**
  1271.      * Writes a String in UTF format.
  1272.      * @param str the String in UTF format
  1273.      * @since     JDK1.1
  1274.      */
  1275.     public void writeUTF(String data) throws IOException {
  1276.     dos.writeUTF(data);
  1277.     }
  1278.     
  1279.     /*************************************************************/
  1280.     
  1281.     /* Remember the first exception that stopped this stream. */
  1282.     private IOException abortIOException = null;
  1283.     
  1284.     /* Write the fields of the specified class.
  1285.      * The native implemention sorts the field names to put them
  1286.      * in cononical order, ignores transient and static fields
  1287.      * and invokes the appropriate write* method on this class.
  1288.      */
  1289.     private native void outputClassFields(Object o, Class cl,
  1290.                       int[] fieldSequence)
  1291.     throws IOException, InvalidClassException;
  1292.  
  1293.     /* Test if Read/WriteObject methods are present, if so
  1294.      * invoke writer and return true.
  1295.      */
  1296.     private native boolean invokeObjectWriter(Object o, Class c)
  1297.     throws IOException;
  1298.  
  1299.     /* Object references are mapped to the wire handles through a hashtable
  1300.      * WireHandles are integers generated by the ObjectOutputStream,
  1301.      * they need only be unique within a stream.
  1302.      * Objects are assigned sequential handles and stored in wireHandle2Object.
  1303.      * The handle for an object is its index in wireHandle2Object.
  1304.      * Object with the "same" hashcode are chained using wireHash2Handle.
  1305.      * The hashcode of objects is used to index through the wireHash2Handle.
  1306.      * -1 is the marker for unused cells in wireNextHandle
  1307.      */
  1308.     private Object[] wireHandle2Object;
  1309.     private int[] wireNextHandle;
  1310.     private int[] wireHash2Handle;
  1311.     private int nextWireOffset;
  1312.  
  1313.     /* The object is the current object and ClassDescriptor is the current
  1314.      * subclass of the object being read. Nesting information is kept
  1315.      * on the stack.
  1316.      */
  1317.     private Object currentObject;
  1318.     private ObjectStreamClass currentClassDesc;
  1319.     private Stack classDescStack;
  1320.  
  1321.     /* 
  1322.      * Flag set to true to allow replaceObject to be called.
  1323.      * Set by enableReplaceObject.
  1324.      * The array of replaceObjects and the index of the next insertion.
  1325.      */
  1326.     boolean enableReplace;
  1327.     private Object[] replaceObjects;
  1328.     private int nextReplaceOffset;
  1329.  
  1330.     /* Recursion level, starts at zero and is incremented for each entry
  1331.      * to writeObject.  Decremented before exit.
  1332.      */ 
  1333.     private int recursionDepth = 0;
  1334.     
  1335.     /*
  1336.      * RMI over IIOP hook: Flag to indicate if we are
  1337.      * a trusted subclass that has implemented the delegate
  1338.      * interface "sun.io.ObjectOutputStreamDelegate".
  1339.      */
  1340.     private boolean isTrustedSubclass = false;
  1341. }
  1342.